#include "LightingCommon.fxh"

cbuffer cbPerRun
{
	float2 gPixelSize;
};

Texture2D gSSAOMap;
Texture2D gDepthTexture;

struct VS_IN
{
	float3 posL     : POSITION;
};

struct VS_OUT
{
	float4 posD         : SV_POSITION;
	noperspective float2 texCoord         : TEXCOORD0;
};

VS_OUT VS(VS_IN vIn)
{
	VS_OUT vOut;

	float4 posH = float4(vIn.posL, 1.0f);
	vOut.posD = posH;

	// SV_POSITION interpolator will be in window coords in PS, so another copy in [0;1] is useful
	vOut.texCoord = (posH / posH.w).xy * float2(0.5f, -0.5f) + float2(0.5f, 0.5f);
	
	return vOut;
}

float PS(VS_OUT pIn) : SV_Target
{
	float depth = gDepthTexture.Sample(gPointSam, pIn.texCoord);
	float3 normalV = getNormalFromGBuffer(pIn.texCoord);

	float totalWeight = 0.0f;

	float result = 0.0f;
	for (float i = -3.5f; i <= 3.5f; i+=1.0f)
	{
		float2 offset = float2(0, 0);
#ifdef VERTICAL
		offset = float2(0.0f, gPixelSize.y * float(i));
#endif
#ifdef HORIZONTAL
		offset = float2(gPixelSize.x * float(i), 0.0f);
#endif

		float depthDiff = abs(depth - gDepthTexture.Sample(gBilinearSam, pIn.texCoord + offset));
		float weight = 1.0f / (0.5f + depthDiff); // MAGIC! - function of SSAO kernel radius! 0.5 for detailed sponza

		//float normalDiff = saturate(dot(normalV, getNormalFromGBuffer(pIn.texCoord + offset)));
		//weight *= normalDiff > 0.95f ? 1.0f : 0.0f;

		totalWeight += weight;

		result += weight * gSSAOMap.Sample(gBilinearSam, pIn.texCoord + offset).r;
	}
	
	result /= totalWeight;
	return result;
}

float PS_H(VS_OUT pIn) : SV_Target
{
	float depth = gDepthTexture.Sample(gPointSam, pIn.texCoord);
	float3 normalV = getNormalFromGBuffer(pIn.texCoord);

	float totalWeight = 0.0f;

	float result = 0.0f;
	for (float i = -3.5f; i <= 3.5f; i+=1.0f)
	{
		float2 offset = float2(gPixelSize.x * float(i), 0.0f);

		float depthDiff = abs(depth - gDepthTexture.Sample(gBilinearSam, pIn.texCoord + offset));
		float weight = 1.0f / (0.5f + depthDiff); // MAGIC! - function of SSAO kernel radius! 0.5 for detailed sponza

		//float normalDiff = saturate(dot(normalV, getNormalFromGBuffer(pIn.texCoord + offset)));
		//weight *= normalDiff > 0.95f ? 1.0f : 0.0f;

		totalWeight += weight;

		result += weight * gSSAOMap.Sample(gBilinearSam, pIn.texCoord + offset).r;
	}
	
	result /= totalWeight;
	return result;
}

float PS_nonseparate(VS_OUT pIn) : SV_Target
{
	float depth = gDepthTexture.Sample(gPointSam, pIn.texCoord);
	float3 normalV = getNormalFromGBuffer(pIn.texCoord);

	float totalWeight = 0.0f;

	float result = 0.0f;
	for (float i = -3.5f; i <= 3.5f; i+=1.0f) {
		for (float j = -3.5f; j <= 3.5f; j+=1.0f) {
			float2 offset = float2(gPixelSize.x * float(j), gPixelSize.y * float(i));

			float depthDiff = abs(depth - gDepthTexture.Sample(gBilinearSam, pIn.texCoord + offset));
			float weight = 1.0f / (1.5f + depthDiff); // MAGIC! - function of SSAO kernel radius! 0.5 for detailed sponza

			//float normalDiff = saturate(dot(normalV, getNormalFromGBuffer(pIn.texCoord + offset)));
			//weight *= normalDiff > 0.95f ? 1.0f : 0.0f;

			totalWeight += weight;


			result += weight * gSSAOMap.Sample(gBilinearSam, pIn.texCoord + offset).r;
		}
	}
	
	result /= totalWeight;
	return result;
}

float PS3(VS_OUT pIn) : SV_Target
{
	return gSSAOMap.Sample(gPointSam, pIn.texCoord).r;
}

float PS2(VS_OUT pIn) : SV_Target
{
	const float kernel[] = { 1.0f / 273.0f, 4.0f / 273.0f, 7.0f / 273.0f, 4.0f / 273.0f, 1.0f / 273.0f,
							 4.0f / 273.0f, 16.0f / 273.0f, 26.0f / 273.0f, 16.0f / 273.0f, 4.0f / 273.0f,
							 7.0f / 273.0f, 26.0f / 273.0f, 41.0f / 273.0f, 26.0f / 273.0f, 7.0f / 273.0f,
							 4.0f / 273.0f, 16.0f / 273.0f, 26.0f / 273.0f, 16.0f / 273.0f, 4.0f / 273.0f,
							 1.0f / 273.0f, 4.0f / 273.0f, 7.0f / 273.0f, 4.0f / 273.0f, 1.0f / 273.0f };

	const float KERNEL_WIDTH = 5;

	float result = 0.0f;
	for (float i = -2.0f; i <= 2.0f; i += 1.0f) {
		for (float j = -2.0f; j <= 2.0f; j += 1.0f) {
			float2 offset = float2(gPixelSize.x * float(j), gPixelSize.y * float(i));
			result += kernel[(i + 2) * KERNEL_WIDTH + (j + 2)] * gSSAOMap.Sample(gPointSam, pIn.texCoord + offset).r;
		}
	}

	return result;
}

technique11 tech
{
	pass P0
	{
		SetVertexShader( CompileShader( vs_5_0, VS() ) );
		SetGeometryShader( NULL );
		SetPixelShader( CompileShader( ps_5_0, PS() ) );
		
		SetDepthStencilState(DepthOff, 0);
		SetRasterizerState(CullBack);
	}
}


